package com.fc.common.aspect;


import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.fc.common.api.constant.CommonConstant;
import com.fc.common.aspect.annotation.AutoLog;
import com.fc.common.util.IPUtil;
import com.fc.common.util.SpringContextUtils;
import com.fc.entity.SysLog;
import com.fc.entity.SysUser;
import com.fc.service.SysLogService;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;


@Component
@Aspect
public class AutoLogAspect {
    @Autowired
    private SysLogService sysLogService;

    @Pointcut("@annotation(com.fc.common.aspect.annotation.AutoLog)")
    public void logPointCut() {
    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("进入方法");

        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //保存日志
        saveSysLog(point, time, result);

        return result;
    }

    public void saveSysLog(ProceedingJoinPoint joinPoint, long time, Object obj){
        SysLog log = new SysLog();
        //获取@AutoLog注解方法 的 MethodSignature
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取方法
        Method method = signature.getMethod();
//        System.out.println("当前执行的方法名(不包括类名)是："+method.getName());
        AutoLog syslog = method.getAnnotation(AutoLog.class);
//        System.out.println("@AutoLog的value属性值："+syslog.value());
        //设置日志内容
        log.setLogContent(syslog.value());
        //设置日志类型
        log.setLogType(syslog.logType());

        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
//        System.out.println("当前执行的完整方法名是："+(className + "." + methodName + "()"));

        //如果是操作日志，设置操作日志的类型
        if(syslog.logType() == CommonConstant.LOG_TYPE_0 ){
            log.setOperateType(getOperateType(methodName,syslog.operateType()));
        }
        SysUser user = null;
        try {
             user = (SysUser) SecurityUtils.getSubject().getPrincipal();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if(user != null){
            log.setRealname(user.getRealname());
        }else {
            log.setRealname("zffc");
        }

        //设置这次请求所花费的时间，单位是毫秒
        log.setCostTime(time);

        //设置该条日志的创建时间
        log.setCreateTime(new Date());


        //获取request,获得本次请求
        HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
//        System.out.println("request："+request);

        //设置ip地址
        log.setIp(IPUtil.getIpAddr(request));

        //解析请求
        String params = getRequestParams(request,joinPoint);
//        System.out.println("本次请求方法参数是："+params);

        //去掉空格
        params = params.replace(" ","");
        //大于100个字符直接舍弃
        if(params.length() > 100){
            params = "";
        }

        //设置本次请求的参数
        log.setRequestParam(params);

        //设置本次请求的类型
        log.setRequestType(request.getMethod());

        //设置本次请求的url
        log.setRequestUrl(request.getRequestURL().toString());
//        System.out.println("请求路径:"+request.getRequestURL().toString());
        //设置本次请求的完全方法名
        log.setMethod((className + "." + methodName + "()"));

        //设置该条日志对应的操作人
        if(user != null ){
            log.setUsername(user.getUsername());
        }else {
            log.setUsername("zffc");
        }

        sysLogService.save(log);


    }

    //获得操作日志的类型
    /**
     * 获取操作类型
     */
    private int getOperateType(String methodName,int operateType) {
        if (operateType > 0) {
            return operateType;
        }
        if (methodName.startsWith("list")) {
            return CommonConstant.OPERATE_TYPE_1;
        }
        if (methodName.startsWith("add")) {
            return CommonConstant.OPERATE_TYPE_2;
        }
        if (methodName.startsWith("delete")) {
            return CommonConstant.OPERATE_TYPE_3;
        }
        if (methodName.startsWith("import")) {
            return CommonConstant.OPERATE_TYPE_4;
        }
        if (methodName.startsWith("export")) {
            return CommonConstant.OPERATE_TYPE_5;
        }
        if (methodName.startsWith("grant")) {
            return CommonConstant.OPERATE_TYPE_6;
        }
        return CommonConstant.OPERATE_TYPE_1;
    }

    //获取请求的参数
    private String getRequestParams(HttpServletRequest request, JoinPoint joinPoint){
        String httpMethod = request.getMethod();
        String params = "";
        if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
            Object[] paramsArray = joinPoint.getArgs();
            // java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
            //  https://my.oschina.net/mengzhang6/blog/2395893
            Object[] arguments  = new Object[paramsArray.length];
            for (int i = 0; i < paramsArray.length; i++) {
                if (paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
                    //ServletRequest不能序列化，从入参里排除，否则报异常：java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
                    //ServletResponse不能序列化 从入参里排除，否则报异常：java.lang.IllegalStateException: getOutputStream() has already been called for this response
                    continue;
                }
                arguments[i] = paramsArray[i];
            }
            //update-begin-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
            PropertyFilter profilter = new PropertyFilter() {
                @Override
                public boolean apply(Object o, String name, Object value) {
                    if(value!=null && value.toString().length()>100){
                        return false;
                    }
                    return true;
                }
            };
            params = JSONObject.toJSONString(arguments, profilter);
            //update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
        } else {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            // 请求的方法参数值
            Object[] args = joinPoint.getArgs();
            // 请求的方法参数名称
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            if (args != null && paramNames != null) {
                for (int i = 0; i < args.length; i++) {
                    params += "  " + paramNames[i] + ": " + args[i];
                }
            }
        }
        return params;
    }

}
